#include <sys/epoll.h>
#include <unistd.h>
#include <assert.h>

#include <cerrno>
#include <cstdlib>
#include <vector>
#include "reactor.h"
#include "eventdispatch.h"

namespace reactor{

EventDispatch::EventDispatch(ReactorPrivate *reactor):m_reactor(reactor)
{
}

EpollEventDispatchImp::EpollEventDispatchImp(ReactorPrivate *reactor) : EventDispatch(reactor),m_fd_size(0){
    m_epoll = epoll_create1(0);
    if(m_epoll == -1){
        perror("epoll error!");
        abort();
    }
}

EpollEventDispatchImp::~EpollEventDispatchImp(){
    close(m_epoll);
}

int EpollEventDispatchImp::addHeap(Event *ev){
    m_time_heap.insert(Node((time(0) + ev->getTimeout()),ev->getHandle()));
    return 0;
}

int EpollEventDispatchImp::removeHeap(Event * ev){
    m_time_heap.remove(Node(0,ev->getHandle()));
    return 0;
}

int EpollEventDispatchImp::add(Event *ev){
    epoll_event event;
    Handle handle = ev->getHandle();
    event.data.fd = handle;
    event.events = 0;

    if(ev->getEventType() & EV_READ)
        event.events |= EPOLLIN;
    if(ev->getEventType() & EV_WRITE)
        event.events |= EPOLLOUT;
    event.events |= EPOLLET;

    if(epoll_ctl(m_epoll,EPOLL_CTL_MOD,handle,&event) != 0){
        if( errno == ENOENT){
            if(epoll_ctl(m_epoll,EPOLL_CTL_ADD,handle,&event) != 0){
                return errno;
            }
            ++m_fd_size;
        }
    }
    return 0;
}

int EpollEventDispatchImp::remove(Event *ev){
    removeHeap(ev);
    int type = ev->getEventType();
    ev->setEventType(type &= (~EV_PERSIST));
    epoll_event ep_evt;
    Handle handle = ev->getHandle();
    if (epoll_ctl(m_epoll, EPOLL_CTL_DEL, handle, &ep_evt) != 0){
        return errno;
    }
    --m_fd_size;
    return 0;
}

int EpollEventDispatchImp::dispatch(std::map<Handle,Event *> * he_map){
    while(true){
        /// 用来接受到来的epoll事件，这里使用真实的大小进行初始化，可以便利性能
        std::vector<epoll_event> events(m_fd_size);
        m_time_heap.test();
        /// 如果事件堆为空，则推出整个循环事件
        if(m_time_heap.size() == 0){
            return -1;
        }
        Node node = m_time_heap.top();
        int current = time(0);
        if(node.m_time < current){
            assert(he_map->find(node.m_handle) != he_map->end());
            Event * ev = (*he_map)[node.m_handle];
            /// 前提：这里为超时事件，未来时间已经小于当前时间了。
            /// TODO 虽然这里逻辑没有问题，但是比较不优雅
            /// 源于这种情况：如果第一次设置为3s，此时紧接着被设置5s，那么就是updateevent=true了，如果还是超时
            /// 那么，就应该把这个设置为false，这样直接判断是否持久。
            /// 按照libevent的尿性，持久与否是在初始化和后来单独设置的。并且这个属性是存在于event中的。
            /// 对于像更新时间的行为，直接找到并更新好了。更新总是以当前的时间值作为已知量
            ev->setUpdateEvent(false);
            ev->timeout(0);
            if(ev->getEventType() & EV_PERSIST || ev->getUpdateEvent()){
                m_time_heap.pop();
                m_time_heap.insert(Node((time(0) + ev->getTimeout()),ev->getHandle()));
                add(ev);
            }else{
                m_time_heap.pop();
                ev->closeEvent();
            }
        } else {
            /// 未超时，则进行等待，如果事件过了，就说明这个事件等到超时了，调用超时即可
            /// 如果是正常事件，那么调用对应的正常事件即可
            /// 如果某个ev处理的事件过长，将导致时间流逝，比如5s中，那么我进行发送数据的时候，这时还没执行到epollwait
            /// 那么5s处理完成之后，发现已经超时了，那么这个epoll—wait还会接受这个事件吗？这里行为好像是如果是持久事件
            /// 重新加入，如果非持久那么就把他从epoll中移除了。那么自然是等待不到的。
            int ret = epoll_wait(m_epoll,&events[0],m_fd_size,(node.m_time-current)*1000);
            std::cout << ret << std::endl;
            if(ret > 0){
                for(int i = 0; i < ret; ++i){
                    epoll_event active_ev = events[i];
                    Handle active_hd = active_ev.data.fd;
                    assert(he_map->find(active_hd) != he_map->end());
                    Event * ev = (*he_map)[active_hd];
                    ev->setUpdateEvent(false);

                    if ((active_ev.events & EPOLLERR) || (active_ev.events & EPOLLHUP)){
                        print_msg("hup");
                        ev->error(0);
                        ev->closeEvent();
                    }
                    if(active_ev.events & EPOLLIN){
                        ev->read(0);
                    }
                    if(active_ev.events & EPOLLOUT){
                        ev->write(0);
                    }

                    if(ev->getEventType() & EV_PERSIST || ev->getUpdateEvent()){
                        m_time_heap.remove(Node(0,active_hd));
                        m_time_heap.insert(Node((time(0) + ev->getTimeout()),active_hd));
                        add(ev);
                        /* In here should update heap
                         * if handle belong to server socket ,you shoud be call on accept,otherwise
                         *it notice to you with stop-ending
                        */
                    }else{
                        m_time_heap.remove(Node(0,active_hd));
                    }
                }
            }else if(ret == 0){//this socket is timeout
                assert(he_map->find(node.m_handle) != he_map->end());
                Event * ev = (*he_map)[node.m_handle];
                ev->setUpdateEvent(false);
                ev->timeout(0);

                if(ev->getEventType() & EV_PERSIST || ev->getUpdateEvent()){
                    m_time_heap.pop();
                    m_time_heap.insert(Node((time(0) + ev->getTimeout()),ev->getHandle()));
                    add(ev);
                }else{
                    m_time_heap.pop();
                    ev->closeEvent();
                }
            }else{
                print_msg("unknow error!");
            }
        }
        std::cout << "--------<" << node.m_handle << ","<< node.m_time << ">--------" << std::endl;
    }
    return 0;
}

}
